use actix_http::StatusCode;
use actix_session::Session;
use actix_web::web::{self, ServiceConfig};
use wither::bson::doc;

use crate::errors::cons_error;
use crate::middlewares::authorize::club_getters::default_getter;
use crate::middlewares::authorize::Authorize;
use crate::models::user::{NormalUserModel, UserActorDTO, UserModelDTO, UserRole};
use crate::models::user_application::UserApplicationModelDTO;
use crate::services::form_service::FormService;
use crate::services::user_service::CreateUserReq;
use crate::services::user_service::GetUserRolesReq;
use crate::services::user_service::UpdateUserInfoReq;
use crate::services::user_service::UserInfoOptions;
use crate::utils::db_utils::{build_filter, COMMON_NO_REGEXP};
use crate::AppError;
use crate::{services::user_service::UserService, AppResult, Pagination, PaginationQuery};
use actix_web::HttpResponse;

pub async fn find_all_users(
    user_service: web::Data<UserService>,
    query: web::Query<PaginationQuery>,
) -> AppResult<Pagination<UserModelDTO>> {
    user_service
        .get_all_users(&query.into_inner())
        .await
        .map(|p| p.map(|v| UserModelDTO::from(v.clone())))
}
pub async fn create_user(
    user_service: web::Data<UserService>,
    query: web::Json<CreateUserReq>,
) -> AppResult<HttpResponse> {
    Ok(HttpResponse::Ok().json(UserModelDTO::from(
        user_service.create_user(&query.into_inner()).await?,
    )))
}

/// ## update_user_info
/// 更新用户信息
pub async fn update_user_info(
    user_service: web::Data<UserService>,
    session: Session,
    req: web::Json<UserInfoOptions>,
) -> AppResult<HttpResponse> {
    let info_options = req.into_inner();
    let user = UserService::get_user_from_session(&session)?;
    let user_id = user
        .id
        .ok_or_else(|| {
            AppError::Status(
                StatusCode::INTERNAL_SERVER_ERROR,
                cons_error::MSG_USER_BREAK.to_string(),
            )
        })?
        .to_string();
    let user = user_service
        .update_user_info(&UpdateUserInfoReq {
            user_id,
            info: info_options,
        })
        .await?;
    UserService::set_user_to_session(&session, &user)?;
    Ok(HttpResponse::NoContent().finish())
}

/// ## get_user_info
/// 获取用户信息
/// `/api/authorize/self` `[get]`
async fn get_user_info(session: Session) -> AppResult<HttpResponse> {
    let user = UserService::get_user_from_session(&session)?;
    Ok(HttpResponse::Ok().json(UserModelDTO::from(user)))
}
/// ## get_user_roles
/// 获取用户权限信息
async fn get_user_roles(
    user_service: web::Data<UserService>,
    session: Session,
    req: web::Query<GetUserRolesReq>,
) -> AppResult<HttpResponse> {
    let user = UserService::get_user_from_session(&session)?;
    let req = req.into_inner();
    let roles = user_service
        .get_user_roles(
            user.id.ok_or_else(|| {
                AppError::Status(
                    StatusCode::INTERNAL_SERVER_ERROR,
                    cons_error::MSG_USER_BREAK.to_string(),
                )
            })?,
            &req,
        )
        .await?;

    Ok(HttpResponse::Ok().json(
        roles
            .into_iter()
            .map(UserActorDTO::from)
            .collect::<Vec<_>>(),
    ))
}

/// ## check_user_role
/// 检查用户权限
async fn check_user_role(
    user_service: web::Data<UserService>,
    session: Session,
) -> AppResult<HttpResponse> {
    let user_id = UserService::get_user_from_session(&session)?
        .id
        .ok_or_else(|| {
            AppError::Status(
                StatusCode::INTERNAL_SERVER_ERROR,
                cons_error::MSG_USER_BREAK.to_string(),
            )
        })?;
    Ok(HttpResponse::Ok().json(user_service.check_user_role(user_id).await?))
}

/// ## find_all_applications
/// 查找当前用户所提交的申请
async fn find_all_applications(
    user: NormalUserModel,
    form_service: web::Data<FormService>,
    page: web::Query<PaginationQuery>,
) -> AppResult<HttpResponse> {
    let filter = doc! {
        "user_id":user.id
    };
    let res = form_service
        .find_all_application(page.into_inner(), filter)
        .await?;
    Ok(HttpResponse::Ok().json(res.map_into::<UserApplicationModelDTO>()))
}

async fn search_all_applications(
    user: NormalUserModel,
    form_service: web::Data<FormService>,
    page: web::Query<PaginationQuery>,
    req: web::Json<ctrl_struct::FindAllApplicationCtrlReq>,
) -> AppResult<HttpResponse> {
    let req = req.into_inner();
    let mut filter = req
        .where_value
        .and_then(|v| build_filter(&v, &COMMON_NO_REGEXP))
        .unwrap_or_default();
    filter.insert("user_id", user.id);
    let res = form_service
        .find_all_application(page.into_inner(), filter)
        .await?;
    Ok(HttpResponse::Ok().json(res.map_into::<UserApplicationModelDTO>()))
}

pub fn configure_user_controller(config: &mut ServiceConfig) {
    config
        .service(
            web::resource("")
                .wrap(Authorize::new(UserRole::Admin, default_getter))
                .route(web::get().to(find_all_users))
                .route(web::post().to(create_user)),
        )
        .service(
            web::resource("/self")
                .wrap(Authorize::new(UserRole::Guest, default_getter))
                .route(web::put().to(update_user_info))
                .route(web::get().to(get_user_info)),
        )
        .service(
            web::resource("/self/role")
                .wrap(Authorize::new(UserRole::Guest, default_getter))
                .route(web::get().to(get_user_roles)),
        )
        .service(
            web::resource("/self/role/check")
                .wrap(Authorize::new(UserRole::Guest, default_getter))
                .route(web::get().to(check_user_role)),
        )
        .service(web::resource("/self/application").route(web::get().to(find_all_applications)))
        .service(
            web::resource("/self/application/search")
                .route(web::post().to(search_all_applications)),
        );
}
mod ctrl_struct {
    use serde::{Deserialize, Serialize};
    use serde_json::Value;
    #[derive(Debug, Clone, Serialize, Deserialize)]
    pub struct FindAllApplicationCtrlReq {
        #[serde(rename = "where")]
        pub where_value: Option<Value>,
    }
}
